/*
* Copyright 2011 Pavel Kozhin 
*
* Licensed under the Apache License, Version 2.0 (the "License"); 
* you may not use this file except in compliance with the License. 
* You may obtain a copy of the License at 
* 
* http://www.apache.org/licenses/LICENSE-2.0
* 
* Unless required by applicable law or agreed to in writing, software 
* distributed  under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
* See the License for the specific language governing permissions and 
* limitations under the License.
*
* Author: Pavel Kozhin.
*/
package org.etherframework.core.bridge
{
	import flash.errors.IllegalOperationError;
	
	import org.etherframework.IEther;
	import org.etherframework.common.DestructionPolicy;
	import org.etherframework.common.IDeferredDestroyable;
	import org.etherframework.common.IDestroyable;
	import org.etherframework.common.IManagedDestroyable;
	import org.etherframework.common.IPersistent;
	import org.etherframework.common.IStatefulDestroyable;
	import org.etherframework.common.IWeaklyDestroyable;
	import org.etherframework.common.wire.IWirable;
	import org.etherframework.common.wire.IWire;
	import org.etherframework.core.common.Ether;
	import org.etherframework.core.common.ether_internal;
	import org.etherframework.core.utils.Dispatcher;
	import org.etherframework.core.utils.utilsClasses.ListenerInfo;
	
	[ExcludeClass]
	public class ObjectController implements IObjectController
	{
		private var _target:Object;
		private var _ether:IEther;
		private var _facade:IBridgeFacades;
		private var _dispatcher:Dispatcher;
		private var _wire:IWire;
		private var _controllers:Controllers;
		private var _listenerInfo:Vector.<ListenerInfo>;
		private var _destroyable:IDestroyable;
		private var _managedDestroyable:IManagedDestroyable;
		private var _deferredDestroyable:IDeferredDestroyable;
		private var _weaklyDestroyable:IWeaklyDestroyable;
		private var _detached:Boolean = true;
		private var _destroyed:Boolean = false;
		private var _initialized:Boolean;
		
		public function ObjectController(target:Object, ether:IEther, facade:IBridgeFacades)
		{
			_target = target;
			_ether = ether;
			_facade = facade;
			_controllers = facade.controllers;
			_dispatcher = facade.dispatcher;
			_destroyable = target as IDestroyable;
			_managedDestroyable = target as IManagedDestroyable;
			_deferredDestroyable = target as IDeferredDestroyable;
			_weaklyDestroyable = target as IWeaklyDestroyable;
		}
		
		public function initialize():void
		{
			if (_initialized)
				throw new IllegalOperationError("IController is already initialized.");
			
			register();
			
			_initialized = true;
		}
		
		protected function register():void
		{
			if (_target is IPersistent)
			{
				_controllers.addController(this);
			}
			
			if (_target is IWirable)
				_wire = _facade.wire(_target as IWirable, _ether);
		}
		
		public function destroy():void
		{
			if (_destroyed)
				throw new IllegalOperationError("IController is already destroyed.");
			
			const e:Ether = _ether as Ether;
			
			if (_controllers.hasController(_target))
				_controllers.removeController(this);
			
			if (_wire)
				_wire.destroy();
			
			if (_destroyable)
			{
				_destroyable.destroy();
			}
			
			if (_destroyable is IStatefulDestroyable)
			{
				IStatefulDestroyable(_destroyable).destroyed = true;
			}
			
			e.ether_internal::destroyed();
			
			_ether = null;
			_wire = null;
			_dispatcher = null;
			_listenerInfo = null;
			_destroyable = null;
			_managedDestroyable = null;
			_deferredDestroyable = null;
			_weaklyDestroyable = null;
			_controllers = null;
		}
		
		public function attach():void
		{
			if (!_detached || _destroyed)
				throw new IllegalOperationError("IController can not be attached.");
			
			if (_listenerInfo)
			{		
				_dispatcher.listenInfo(_ether, _listenerInfo);
				_listenerInfo = null;
			}
			
			if (_weaklyDestroyable)
			{
				_weaklyDestroyable.attach();
			}
		}
		
		public function detach():void
		{
			if (_detached || _destroyed)
				throw new IllegalOperationError("IController can not be detached.");
			
			_listenerInfo = _dispatcher.unlistenAll(_ether);
			
			if (_weaklyDestroyable)
			{
				_weaklyDestroyable.detach();
			}
		}
		
		public function persisted():void
		{
			if (_target is IPersistent)
			{
				_target.persisted();
			}
		}
		
		public function get detached():Boolean { return _detached; }
		public function set detached(value:Boolean):void 
		{ 
			if (_weaklyDestroyable)  
				_weaklyDestroyable.detached = value;
			_detached = value;
		}

		public function get destroyed():Boolean { return _destroyed; }
		public function set destroyed(value:Boolean):void 
		{ 
			if (_destroyable is IStatefulDestroyable)
				IStatefulDestroyable(_destroyable).destroyed = value;
			
			_destroyed = value;
		}
		
		public function get destructionPolicy():String 
		{ 
			return (_managedDestroyable) ? 
				_managedDestroyable.destructionPolicy : DestructionPolicy.AUTO; 
		}
		public function set destructionPolicy(value:String):void 
		{ 
			if (_managedDestroyable)
				_managedDestroyable.destructionPolicy = value;
		}
		
		public function get destructionDelay():uint
		{
			return (_deferredDestroyable) ?  
				_deferredDestroyable.destructionDelay : 0;
		}

		public function set destructionDelay(value:uint):void
		{
			if (_deferredDestroyable)
				_deferredDestroyable.destructionDelay = value;
		}
		
		public function get destructionCondition():Boolean 
		{
			return (_deferredDestroyable) ? 
				_deferredDestroyable.destructionCondition : true;
		}
		
		public function set destructionCondition(value:Boolean):void 
		{
			if (_deferredDestroyable)
				_deferredDestroyable.destructionCondition = value;
		}
		
		public function get target():Object { return _target }
		public function get ether():IEther { return _ether }
		public function get wire():IWire { return _wire; }
	}
}